<dom-module id="x-host">
  <template>

    <x-templatizer obj="{{objA}}" prop="{{propA}}" id="templatizer">
      <template>
        <x-child id="childA"
          outer-prop="{{outerProp}}"
          outer-obj="{{outerObj}}"
          outer-obj-prop="{{outerObj.prop}}"
          prop="{{prop}}"
          obj="{{obj}}"
          obj-prop="{{obj.prop}}"
          conflict="{{outerInnerConflict.prop}}"
          computed-from-literal="{{computeFromLiteral(33, prop)}}"
        ></x-child>
      </template>
    </x-templatizer>

    <template is="x-templatizee" obj="{{objB}}" prop="{{propB}}" id="templatizee">
      <x-child id="childB"
        outer-prop="{{outerProp}}"
        outer-obj="{{outerObj}}"
        outer-obj-prop="{{outerObj.prop}}"
        prop="{{prop}}"
        obj="{{obj}}"
        obj-prop="{{obj.prop}}"
        conflict="{{outerInnerConflict.prop}}"
      ></x-child>
    </template>

  </template>
</dom-module>

<script>
  Polymer({
    is: 'x-child',
    properties: {
      outerProp: {
        notify: true
      },
      outerObj: {
        notify: true
      },
      outerObjProp: {
        notify: true
      },
      prop: {
        notify: true
      },
      obj: {
        notify: true
      },
      objProp: {
        notify: true
      },
      outerInnerConflict: {
        notify: true
      }
    },
    observers: [
      'objChanged(obj.*)',
      'outerObjChanged(outerObj.*)'
    ],
    objChanged: function() {},
    outerObjChanged: function() {}
  });

  Polymer({
    is: 'x-templatizer',
    behaviors: [Polymer.Templatizer],
    properties: {
      obj: {
        notify: true
      },
      prop: {
        notify: true,
        observer: 'propChanged'
      }
    },
    observers: [
      'objChanged(obj.*)'
    ],
    _instanceProps: {
      obj: true,
      prop: true,
      outerInnerConflict: true
    },
    propChanged: function(value) {
      this._forwardParentProp('prop', value);
    },
    objChanged: function(info) {
      if (info.path == 'obj') {
        this._forwardParentProp('obj', info.value);
      } else {
        this._forwardParentPath(info.path, info.value);
      }
    },
    _forwardParentProp: function(prop, value) {
      if (this.instance) {
        this.instance.__setProperty(prop, value, true);
      }
    },
    _forwardParentPath: function(path, value) {
      if (this.instance) {
        this.instance.notifyPath(path, value, true);
      }
    },
    _forwardInstanceProp: function(inst, prop, value) {
      if (prop == 'obj') {
        this.obj = value;
      } else if (prop == 'prop') {
        this.prop = value;
      }
    },
    _forwardInstancePath: function(inst, path, value) {
      if ((path.indexOf('obj.') === 0) || (path.indexOf('prop.') === 0)) {
        this.notifyPath(path, value);
      }
    },
    go: function() {
      var template = Polymer.dom(this).querySelector('template');
      this.templatize(template);
      this.instance = this.stamp({
        obj: this.obj,
        prop: this.prop,
        outerInnerConflict: {
          prop: 'bar'
        }
      });
      var parent = Polymer.dom(this).parentNode;
      Polymer.dom(parent).appendChild(this.instance.root);
    }
  });

  Polymer({
    is: 'x-templatizee',
    extends: 'template',
    behaviors: [Polymer.Templatizer],
    properties: {
      obj: {
        notify: true
      },
      prop: {
        notify: true,
        observer: 'propChanged'
      }
    },
    observers: [
      'objChanged(obj.*)'
    ],
    _instanceProps: {
      obj: true,
      prop: true,
      outerInnerConflict: true
    },
    propChanged: function(value) {
      this._forwardParentProp('prop', value);
    },
    objChanged: function(info) {
      if (info.path == 'obj') {
        this._forwardParentProp('obj', info.value);
      } else {
        this._forwardParentPath(info.path, info.value);
      }
    },
    _forwardParentProp: function(prop, value) {
      if (this.instance) {
        this.instance.__setProperty(prop, value, true);
      }
    },
    _forwardParentPath: function(path, value) {
      if (this.instance) {
        this.instance.notifyPath(path, value, true);
      }
    },
    _forwardInstanceProp: function(inst, prop, value) {
      if (prop == 'obj') {
        this.obj = value;
      } else if (prop == 'prop') {
        this.prop = value;
      }
    },
    _forwardInstancePath: function(inst, path, value) {
      if ((path.indexOf('obj.') === 0) || (path.indexOf('prop.') === 0)) {
        this.notifyPath(path, value);
      }
    },
    go: function() {
      this.templatize(this);
      this.instance = this.stamp({
        obj: this.obj,
        prop: this.prop,
        outerInnerConflict: {
          prop: 'bar'
        }
      });
      var parent = Polymer.dom(this).parentNode;
      Polymer.dom(parent).appendChild(this.instance.root);
    }
  });

  Polymer({
    is: 'x-host',
    properties: {
      outerProp: {
        value: 'outerProp'
      },
      outerObj: {
        value: function() {
          return { prop: 'outerObj.prop' };
        }
      },
      propA: {
        value: 'prop-a'
      },
      objA: {
        value: function() {
          return { prop: 'objA.prop' };
        }
      },
      propB: {
        value: 'prop-b'
      },
      objB: {
        value: function() {
          return { prop: 'objB.prop' };
        }
      },
      outerInnerConflict: {
        value: function() {
          return { prop: 'conflict' };
        }
      }
    },
    observers: [
      'outerObjChanged(outerObj.*)',
      'objAChanged(objA.*)',
      'objBChanged(objB.*)'
    ],
    outerObjChanged: function() {},
    objAChanged: function() {},
    objBChanged: function() {},
    computeFromLiteral: function() {}
  });

</script>

